﻿Imports System.Drawing.Printing
Imports System.Management
Imports System.Drawing.Drawing2D
Imports System.ComponentModel
Imports System.Threading
Imports DAL
Imports System.Globalization

Public Class FrmImprimeHoraire


#Region "native functions"
    Private Declare Auto Function GlobalLock Lib "kernel32.dll" _
    (ByVal handle As IntPtr) As IntPtr
    Private Declare Auto Function GlobalUnlock Lib "kernel32.dll" _
    (ByVal handle As IntPtr) As Integer
    Private Declare Auto Function GlobalFree Lib "kernel32.dll" _
    (ByVal handle As IntPtr) As IntPtr
    Private Declare Auto Function DocumentProperties Lib "winspool.drv" _
    (ByVal hWnd As IntPtr, ByVal hPrinter As IntPtr, _
    ByVal pDeviceName As String, ByVal pDevModeOutput As IntPtr, _
    ByVal pDevModeInput As IntPtr, ByVal fMode As Int32) As Integer
#End Region

    Private Structure Printer
        Public Name As String
        Public Status As Integer
        Public Comment As String
    End Structure
    Private document As New PrintDocument
    Private PrinterList As New List(Of Printer)
    Private renderThread As Thread
    Private renderImage As Bitmap = New Bitmap(262, 262)
    Private loadingImage As Bitmap = New Bitmap(262, 262)
    Private pageCount As Integer = 1
    Private pageNumber As Integer = 1
    Private pageNoteCount As Integer = 0
    Private _DateHoraire As Date = Date.Now
    Private dbc As New BDCarpeDiemDataContext
    Private PrintStarted As Boolean = False
    Public Property DateHoraire() As Date
        Get
            Return _DateHoraire
        End Get
        Set(ByVal value As Date)
            _DateHoraire = value
        End Set
    End Property

    Private listePlages As New List(Of PlageHoraire)
    Private listeEmployes As New List(Of Employe)
    Private listeTaches As New List(Of Tache)
    Private listeFormat As New List(Of PaperSize)
    Private strFormat As New StringFormat
    Private animFrame As Integer = 0

    Structure Reference
        Public Numero As Integer
        Public PlageID As Integer
        Public NomPlage As String
        Public NomEmp As String
        Public Desc As String
        Public Heures As String
    End Structure
    Private listePlagesReference As New List(Of Reference)
    Private PageFont As Font
    Private Sub FrmImprimeHoraire_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        'Dim pkInstalledPrinters As String
        document.DefaultPageSettings.Landscape = True
        document.PrinterSettings.DefaultPageSettings.Landscape = True
        Dim impDefaut As Integer = -1
        ' Find all printers installed
        Try
            Dim searcher As New ManagementObjectSearcher( _
                "root\CIMV2", _
                "SELECT * FROM Win32_Printer")

            For Each queryObj As ManagementObject In searcher.Get()
                Dim prin As New Printer
                prin.Name = queryObj("Caption").ToString()
                prin.Status = Convert.ToInt32(queryObj("PrinterStatus"))
                prin.Comment = IIf(queryObj("Comment") Is Nothing, String.Empty, queryObj("Comment"))
                PrinterList.Add(prin)
                
                Dim itm As New ListViewItem(prin.Name)
                itm.ImageIndex = 0
                ListView1.Items.Add(itm)
                If prin.Name = document.PrinterSettings.PrinterName Then
                    impDefaut = ListView1.Items.IndexOf(itm)
                End If
            Next
        Catch err As ManagementException
            MessageBox.Show("An error occurred while querying for WMI data: " & err.Message)
        End Try
        If impDefaut <> -1 Then
            ListView1.Items(impDefaut).Selected = True
            ListView1.Select()
        End If
        FillLists()

        AddHandler document.PrintPage, AddressOf PrintHoraire
        renderThread = New Thread(AddressOf RenderPreview)

        PageFont = Me.Font

        'Code pris de l'ouverture du dialogue de paramètres de l'imprimante (un peu plus bas)
        'J'ai aucune idée pourquoi et qu'est-ce que ca fait, mais pour une raison obscure
        'l'affichage de l'aperçu d'impression est super rapide après avoir ouvert le dialogue de l'imprimante
        'Donc j'ai copié un bout ici et ca fonctionne
        Dim hDevMode As IntPtr = _
        document.PrinterSettings.GetHdevmode(document.PrinterSettings.DefaultPageSettings)
        Dim pDevMode As IntPtr = GlobalLock(hDevMode)
        GlobalUnlock(hDevMode)
        document.PrinterSettings.SetHdevmode(hDevMode)
        document.PrinterSettings.DefaultPageSettings.SetHdevmode(hDevMode)
        GlobalFree(hDevMode)

        Cal_Horaire.Value = DateHoraire
        Cal_Horaire.GoToValue()

        For Each formatPapier As PaperSize In document.PrinterSettings.PaperSizes
            CboFormatPapier.Items.Add(formatPapier.PaperName)
            listeFormat.Add(formatPapier)
        Next
        Console.WriteLine(document.DefaultPageSettings.PaperSize)
        CboFormatPapier.SelectedItem = document.DefaultPageSettings.PaperSize.PaperName
    End Sub

    Private Sub ListView1_ItemSelectionChanged(sender As System.Object, e As System.Windows.Forms.ListViewItemSelectionChangedEventArgs) Handles ListView1.ItemSelectionChanged
        If ListView1.SelectedItems.Count = 1 Then
            Dim psettinng As New PrinterSettings
            psettinng.PrinterName = ListView1.SelectedItems(0).Text
            Dim statut As String = String.Empty
            Select Case PrinterList(ListView1.SelectedIndices(0)).Status
                Case 1
                    statut = "Hors ligne"
                Case 2
                    statut = "lol"
                Case 3
                    statut = "Prêt"

            End Select
            Label1.Text = "Statut : " + statut
            document.PrinterSettings.PrinterName = ListView1.SelectedItems(0).Text
            'If Not renderThread Is Nothing Then
            '    renderThread = New Thread(AddressOf RenderPreview)
            '    renderThread.Start()
            'End If
        End If

    End Sub

    Private Sub Btn_PrefImp_Click(sender As System.Object, e As System.EventArgs) Handles Btn_PrefImp.Click
        If (ListView1.SelectedItems.Count = 1) Then
            If (document.PrinterSettings.PrinterName <>
            ListView1.SelectedItems(0).Text) Then
                document.PrinterSettings.PrinterName =
                ListView1.SelectedItems(0).Text
            End If
            If document.PrinterSettings.IsValid Then
                Me.OpenPrinterPropertiesDialog(document.PrinterSettings)
                StartRender()
            Else
                MsgBox("Imprimante invalide", MsgBoxStyle.Information)
            End If
        Else
            MsgBox("Sélectionnez une imprimante", MsgBoxStyle.Information)
        End If
    End Sub

    Sub OpenPrinterPropertiesDialog(ByVal Settings As PrinterSettings)
        ' PrinterSettings+PageSettings -> hDEVMODE
        Dim hDevMode As IntPtr = _
        Settings.GetHdevmode(Settings.DefaultPageSettings)

        ' Show Dialog ( [In,Out] pDEVMODE )
        Dim pDevMode As IntPtr = GlobalLock(hDevMode)
        DocumentProperties(Me.Handle, IntPtr.Zero, _
        Settings.PrinterName, pDevMode, pDevMode, 14)
        GlobalUnlock(hDevMode)

        ' hDEVMODE -> PrinterSettings+PageSettings
        Settings.SetHdevmode(hDevMode)
        Settings.DefaultPageSettings.SetHdevmode(hDevMode)

        ' cleanup
        GlobalFree(hDevMode)
        CboFormatPapier.SelectedItem = document.DefaultPageSettings.PaperSize.PaperName
        Nud_NbCopie.Value = document.PrinterSettings.Copies
    End Sub

    Private Sub UcCalendrier1_Value_Changed(sender As System.Object, e As System.EventArgs) Handles Cal_Horaire.Value_Changed

        document.DefaultPageSettings = document.PrinterSettings.DefaultPageSettings
        StartRender()
    End Sub

    Private Sub StartRender()
        
        If Not renderThread Is Nothing Then
            If renderThread.IsAlive Then
                renderThread.Join()
            End If
            animFrame = 0
            'AnimationTimer.Start()
            pageNumber = 0
            pageCount = 1
            pageNoteCount = 0
            FillLists()
            If Not renderThread.IsAlive Then
                renderThread = New Thread(AddressOf RenderPreview)
                renderThread.Name = "RenderThread"
                renderThread.Start()
            End If
            'renderThread.Join()
            'Cal_Horaire.Enabled = True
        End If
        'AnimationTimer.Stop()
    End Sub

    Private Sub RenderPreview()
        Using g As Graphics = Graphics.FromImage(renderImage)

            Dim rect As Rectangle = New Rectangle(0, 0, document.PrinterSettings.DefaultPageSettings.PaperSize.Width, _
                                                  document.PrinterSettings.DefaultPageSettings.PaperSize.Height)
            'rétrécir le recangle de la page pour entrer dans le picutrebox
            If rect.Width > rect.Height Then
                rect.Height = (250 / rect.Width) * rect.Height
                rect.Width = 250
            Else
                rect.Width = (250 / rect.Height) * rect.Width
                rect.Height = 250
            End If
            'Corrige l'orientation portrait/paysage
            If document.PrinterSettings.DefaultPageSettings.Landscape Then
                Dim old As Integer = rect.Width
                rect.Width = rect.Height
                rect.Height = old
            End If
            rect.X = (260 - rect.Width) / 2
            rect.Y = (260 - rect.Height) / 2
            g.Clear(SystemColors.ControlDark)
            For i = 1 To 10
                Dim r As New GraphicsPath
                r.AddRectangle(rect)
                r.Widen(New Pen(Brushes.Black, i))
                g.FillPath(New SolidBrush(Color.FromArgb(10, 0, 0, 0)), r)
            Next
            Dim pSize As Rectangle = document.DefaultPageSettings.Bounds
            g.FillRectangle(Brushes.White, rect)
            Dim imgHoraire As New Bitmap(pSize.Width, pSize.Height)
            Using g2 As Graphics = Graphics.FromImage(imgHoraire)
                g2.Clear(Color.White)
                PaintHoraire(g2, document.PrinterSettings.DefaultPageSettings)
            End Using
            g.DrawImage(imgHoraire, rect)
        End Using

        AnimationTimer.Stop()
        Using g As Graphics = PictureBox1.CreateGraphics()
            g.DrawImage(renderImage, 0, 0)
        End Using



    End Sub

    Private Sub PaintHoraire(ByVal g As Graphics, ByVal pSetting As PageSettings)
        g.SmoothingMode = SmoothingMode.HighQuality
        strFormat.LineAlignment = StringAlignment.Center
        strFormat.Alignment = StringAlignment.Center
        Dim Semaine = Cal_Horaire.Value
        Dim wNom As Single = 0 'Largeur de la colonne des noms
        Dim wCol As Single = 0 'Largeur des autre colonnes (jours)
        Dim hHead As Single = 0 'Hauteur de l'entete
        Dim hRow As Single = 0 'Hauteur d'une rangée
        Dim pMargin As Margins = pSetting.Margins 'Les marges de la page
        Dim couleur As Boolean = Ckb_Couleur.Checked 'Nom de variable plus court pour l'affichage des couleurs
        Dim notes As Boolean = Ckb_DescTaskRef.Checked 'Nom de variable plus court pour les notes et taches
        'Rectangle du contenu de la page (rectangle dela page moins les marges
        Dim contentBound As New RectangleF(pMargin.Left, pMargin.Top, _
                                           pSetting.Bounds.Width - (pMargin.Left + pMargin.Right), _
                                           pSetting.Bounds.Height - pMargin.Top - pMargin.Bottom)
        Dim endPage As Boolean = False 'Variable de verification si la page est pleine
        'Sert si le contenu depasse la page, pour n'afficher seulement ce qui reste sur une autre page
        Dim EmpToRemove As New List(Of Integer) 'Employés a enlever 
        Dim PlageToRemove As New List(Of Integer) 'Plage horaire a enlever
        Dim TacheToRemove As New List(Of Integer) 'Taches a enlever
        Dim ReferenceToRemove As New List(Of Integer) 'References a enlever
        'Defenition de la largeur de la colonne des noms selon le plus long nom
        For Each emp In listeEmployes
            Dim w As Single = g.MeasureString(emp.Nom + ", " + emp.Prenom, PageFont).Width
            If w > wNom Then
                wNom = w
            End If
        Next
        wNom += 5 'extra padding
        wCol = (contentBound.Width - wNom) / 7 'les autre colonnes se divisent le restant de la page

        hHead = g.MeasureString("Vendredi" + Environment.NewLine + "30 Septembre", PageFont, wCol).Height + 5 'Calcul de la hauteur de l'entete

        Dim nextHeight As Single = contentBound.Y 'Variable contenant la hauteur (position Y) du dernier élément affiché
        'Entete
        Dim txtEntete As String = "Horaire de la semaine du " + Cal_Horaire.Value.Day.ToString + " " + Cal_Horaire.Value.ToString("MMMM", CultureInfo.GetCultureInfo("fr-CA"))
        g.DrawString(txtEntete, New Font(Me.Font.FontFamily, 12), Brushes.Black, contentBound.X, contentBound.Y)
        Dim txtHeadHeight As Single = g.MeasureString(txtEntete, New Font(Me.Font.FontFamily, 14)).Height
        nextHeight += txtHeadHeight

        hRow = g.MeasureString("intervention" + Environment.NewLine + "12:50-12:50", PageFont, wCol).Height + 5 'Definition de la hauteur moyenne d'une rangée

        If listeEmployes.Count > 0 Or listePlages.Count > 0 Then 'sert a ne pas dessiner l'entete quand c'est rendu aux références
            'Jours de la semaine
            g.DrawLine(Pens.Black, contentBound.X, nextHeight, contentBound.Width + contentBound.X, nextHeight)
            g.DrawLine(Pens.Black, contentBound.X, nextHeight + hHead, contentBound.Width + contentBound.X, nextHeight + hHead)

            For i As Integer = 0 To 6
                Dim jourRect As New RectangleF(contentBound.X + wNom + wCol * i, nextHeight, wCol, hHead)
                Dim texte As String = StrConv(Semaine.AddDays(-Semaine.DayOfWeek + i).ToString("dddd", CultureInfo.GetCultureInfo("fr-CA")), VbStrConv.ProperCase) _
                                     + Environment.NewLine + Semaine.AddDays(-Semaine.DayOfWeek + i).Day.ToString() _
                                     + " " + StrConv(Semaine.AddDays(-Semaine.DayOfWeek + i).ToString("MMMM", CultureInfo.GetCultureInfo("fr-CA")), VbStrConv.ProperCase)
                g.DrawString(texte, PageFont, Brushes.Black, jourRect, strFormat)
            Next
            nextHeight += hHead
        End If

        'Employes et plages
        strFormat.Alignment = StringAlignment.Near
        Dim empCtr As Integer = 0
        For Each emp In listeEmployes

            Dim nomRect As New RectangleF(contentBound.X + 3, nextHeight, wNom, hRow)
            g.DrawString(emp.Nom.TrimStart() + ", " + emp.Prenom, PageFont, Brushes.Black, nomRect, strFormat)
            Dim eID = emp.ID
            Dim plageDate As Date = Date.Now
            Dim plageDateFin As Date = Date.Now
            Dim plageRect As New RectangleF(contentBound.X + wNom + 3, nextHeight, wCol, hRow)
            Dim plageParJourOffset(7) As Single
            Dim startHeight As Single = nextHeight
            Dim plageHeight As Single = hRow

            'Dessin des plage
            For Each plage In listePlages.Where(Function(X) X.Employe = eID)
                'positionnement de la plage
                plageRect.Y = nextHeight + plageParJourOffset(plage.Date.DayOfWeek)
                plageRect.X = contentBound.X + wNom + 3 + wCol * plage.Date.DayOfWeek
                plageDate = plage.Date
                plageDateFin = plageDate.AddMinutes(plage.Duree)

                Dim haveDetail As Boolean = False
                If notes AndAlso Not plage.Note Is Nothing AndAlso Not plage.Note.Trim() = String.Empty Then

                    haveDetail = True
                End If
                If notes AndAlso Not plage.Taches Is Nothing AndAlso plage.Taches.Count > 0 Then
                    haveDetail = True
                End If

                Dim heureTxt As String = plageDate.ToString("H':'mm") + "-" + plageDateFin.ToString("H':'mm")
                Dim plageTxt As String = plage.Nom

                If plage.Accompagnement.HasValue AndAlso plage.Accompagnement Then
                    plageTxt += Environment.NewLine + plage.Beneficiaire1.Nom + ", " + plage.Beneficiaire1.Prenom
                End If
                If plage.QuartArriere.HasValue AndAlso plage.QuartArriere Then
                    plageTxt += " [Q]"
                End If
                plageTxt += Environment.NewLine + heureTxt
                plageHeight = g.MeasureString(plageTxt + IIf(haveDetail, "(" + (pageNoteCount + 1).ToString + ")", ""), PageFont, plageRect.Width).Height + 5
                'Verifier si le texte de cette plage va dépasser les marges, si oui : continu de dessiner les autres plage qui ne depassent pas
                'si non : ajouter la plage a la liste de plage a enlever et la dessine
                If plageParJourOffset(plage.Date.DayOfWeek) + nextHeight + plageHeight > contentBound.Height + contentBound.Y Then
                    endPage = True
                    Continue For
                Else
                    PlageToRemove.Add(plage.ID)
                    plageParJourOffset(plage.Date.DayOfWeek) += plageHeight
                End If
                plageRect.Height = plageHeight

                If couleur Then
                    Dim col As Color = ColorTranslator.FromHtml(plage.Couleur)
                    col = Color.FromArgb(100, col.R, col.G, col.B)
                    g.FillRectangle(New SolidBrush(col), New RectangleF(plageRect.X - 3, plageRect.Y, plageRect.Width, plageRect.Height))
                End If

                g.DrawString(plageTxt, PageFont, Brushes.Black, plageRect, strFormat)

                If haveDetail Then 'si la plage contient une description ou des taches, les ajouter a la liste de references pour afficher plus tard (après l'horaire)
                    Dim ref As New Reference
                    ref.Numero = (pageNoteCount + 1)
                    ref.PlageID = plage.ID
                    ref.Desc = plage.Note
                    ref.NomEmp = plage.Employe1.Nom.TrimStart() + ", " + plage.Employe1.Prenom
                    ref.NomPlage = plage.Nom
                    ref.Heures = heureTxt
                    listePlagesReference.Add(ref)
                    strFormat.Alignment = StringAlignment.Center
                    Dim noteRect As New RectangleF(plageRect.X + wCol - PageFont.Height - 6, plageRect.Y + plageRect.Height - PageFont.Height - 3, PageFont.Height, PageFont.Height)
                    g.DrawEllipse(Pens.Black, noteRect)
                    g.DrawString((pageNoteCount + 1).ToString, New Font(PageFont.FontFamily, 6), Brushes.Black, noteRect, strFormat)
                    strFormat.Alignment = StringAlignment.Near
                End If

                If haveDetail Then
                    pageNoteCount += 1
                End If
            Next

            Dim nbPlage As Integer = 1
            Dim maxH As Single = hRow
            For i = 0 To 6
                If plageParJourOffset(i) > maxH Then
                    maxH = plageParJourOffset(i)
                End If
            Next

            If Not endPage AndAlso nextHeight + maxH > contentBound.Height + contentBound.Y Then 'verification si le contenu depasse
                endPage = True
                'Erase 
                If empCtr > 1 Then 'la hauteur de la rangé étant calculé après l'affichage, on efface ce qui est dessiné qui a dépassé
                    g.FillRectangle(Brushes.White, 0, nextHeight, pSetting.Bounds.Width, maxH + 2)
                End If
            Else
                If Not endPage Then
                    EmpToRemove.Add(emp.ID)
                End If
                nextHeight += maxH
            End If

            If Not endPage Then
                g.DrawLine(Pens.Black, contentBound.X, nextHeight, _
                       contentBound.Width + contentBound.X, nextHeight)
            End If

            empCtr += 1
            If endPage Then
                pageCount += 1
                Exit For
            End If
        Next


        'Lignes Verticales
        If listeEmployes.Count > 0 Or listePlages.Count > 0 Then 'dessin des bordure de l'horaire
            For i As Integer = 0 To 6
                g.DrawLine(Pens.Black, contentBound.X + wNom + wCol * i, contentBound.Y + txtHeadHeight, contentBound.X + wNom + wCol * i, nextHeight)
            Next
            g.DrawRectangle(Pens.Black, contentBound.X, contentBound.Y + txtHeadHeight, contentBound.Width, nextHeight - contentBound.Y - txtHeadHeight)

        ElseIf notes Then 'affichage des notes et références
            Dim oldPlage = 0
            g.DrawString("Tâches et descriptions", New Font(Me.Font.FontFamily, 10), Brushes.Black, contentBound.X, nextHeight)
            nextHeight += g.MeasureString("Tâches et descriptions", New Font(Me.Font.FontFamily, 10)).Height + 5
            Dim tacheDepasse As Boolean = False

            Dim hl As Single = g.MeasureString("Allo", PageFont).Height + 2 'hauteur ligne
            For Each ref In listePlagesReference

                Dim hauteurRefPlage As Single = 0
                hauteurRefPlage += hl
                If Not ref.Desc Is Nothing And Not ref.Desc = String.Empty Then
                    hauteurRefPlage += g.MeasureString("Description : " + ref.Desc, PageFont).Height
                End If

                hauteurRefPlage += hl / 2 'bottom margin
                If nextHeight + hauteurRefPlage > contentBound.Height + contentBound.Y Then
                    Exit For
                End If

                strFormat.Alignment = StringAlignment.Center
                Dim noteRect As New RectangleF(contentBound.X + 5, nextHeight, PageFont.Height, PageFont.Height)
                g.DrawEllipse(Pens.Black, noteRect)
                g.DrawString(ref.Numero.ToString, New Font(PageFont.FontFamily, 6), Brushes.Black, noteRect, strFormat)

                strFormat.Alignment = StringAlignment.Near

                g.DrawString(ref.NomPlage + " par " + ref.NomEmp + " de " + ref.Heures, PageFont, Brushes.Black, contentBound.X + PageFont.Height + 10, nextHeight)
                nextHeight += hl
                If Not ref.Desc Is Nothing And Not ref.Desc = String.Empty Then
                    Dim descW As Single = g.MeasureString("Description : ", PageFont).Width
                    g.DrawString("Description : ", PageFont, Brushes.Black, contentBound.X + PageFont.Height + 10, nextHeight)
                    g.DrawString(ref.Desc, PageFont, Brushes.Black, contentBound.X + PageFont.Height + 10 + descW, nextHeight)
                    nextHeight += g.MeasureString("Description : " + ref.Desc, PageFont).Height + PageFont.Height / 2
                End If

                Dim pid = ref.PlageID
                Dim first As Boolean = True
                Dim offsetX As Integer = contentBound.X + PageFont.Height + 10
                Dim offsetY As Integer = nextHeight
                Dim largeurBlocTache As Integer = g.MeasureString("l", PageFont).Width * 25
                Dim maxH As Integer = 0
                tacheDepasse = False
                For Each tache In listeTaches.Where(Function(x) x.Plage = pid)

                    Dim hauteurBlocTache As Single = 0
                    hauteurBlocTache += IIf(first, hl, 0)
                    hauteurBlocTache += hl
                    hauteurBlocTache += IIf(tache.Duree > 0, hl, 0)
                    If Not tache.Description Is Nothing AndAlso tache.Description <> String.Empty Then
                        hauteurBlocTache += g.MeasureString("Desc:" + tache.Description, PageFont, largeurBlocTache).Height
                    End If

                    Console.WriteLine("h = " + hauteurBlocTache.ToString)
                    If offsetY + hauteurBlocTache > contentBound.Height + contentBound.Y Then
                        'Console.WriteLine("depasse offY = " + hauteurBlocTache.ToString)
                        tacheDepasse = True
                        Continue For
                    End If

                    If first Then
                        g.DrawString("Tâche(s) : ", PageFont, Brushes.Black, offsetX, offsetY)
                        nextHeight += hl
                        offsetY = nextHeight
                        first = False
                    End If

                    TacheToRemove.Add(tache.ID)

                    g.FillEllipse(Brushes.Black, _
                                  New Rectangle(offsetX + PageFont.Height / 3, _
                                                offsetY + (hl - PageFont.Height / 3) / 2, hl / 3, hl / 3))
                    'g.DrawRectangle(Pens.Red, offsetX + hFont.Height, offsetY, largeurBlocTache, hFont.Height + 5)
                    g.DrawString(tache.Nom, PageFont, Brushes.Black, offsetX + PageFont.Height, offsetY)
                    offsetY += hl
                    If Not tache.Description Is Nothing AndAlso tache.Description <> String.Empty Then
                        Dim hauteurDesc As Single = g.MeasureString("Desc:" + tache.Description, PageFont, largeurBlocTache).Height
                        g.DrawString("Desc:" + tache.Description, PageFont, Brushes.Black, New Rectangle(offsetX + PageFont.Height, offsetY, largeurBlocTache, hauteurDesc))
                        offsetY += hauteurDesc
                    End If
                    If tache.Duree > 0 Then
                        Dim dureeTxt As String = Date.Today.AddMinutes(tache.Duree).ToString("H'h'mm")
                        g.DrawString("Durée : " + dureeTxt, PageFont, Brushes.Black, offsetX + PageFont.Height, offsetY)
                        offsetY += hl
                    End If

                    maxH = Math.Max(maxH, offsetY - nextHeight)
                    offsetX += largeurBlocTache
                    If offsetX + largeurBlocTache > contentBound.Width + contentBound.X Then
                        offsetX = contentBound.X + PageFont.Height + 10
                        nextHeight += maxH
                        maxH = 0
                    End If
                    offsetY = nextHeight

                Next
                nextHeight += maxH + 5
                If Not tacheDepasse Then
                    ReferenceToRemove.Add(ref.Numero)
                End If

            Next

            listeTaches.RemoveAll(Function(x) TacheToRemove.Contains(x.ID))
            listePlagesReference.RemoveAll(Function(x) ReferenceToRemove.Contains(x.Numero))
            If tacheDepasse Then
                Dim tref As Reference = listePlagesReference(0)
                tref.Desc = String.Empty
                listePlagesReference.RemoveAt(0)
                listePlagesReference.Insert(0, tref)
            End If
            'listeTaches.RemoveAll(Function(x) TacheToRemove.Contains(x.ID))
        End If
        listeEmployes.RemoveAll(Function(x) EmpToRemove.Contains(x.ID))
        listePlages.RemoveAll(Function(x) PlageToRemove.Contains(x.ID))
        'g.DrawRectangle(Pens.Red, contentBound.X, contentBound.Y, contentBound.Width, contentBound.Height)
    End Sub

    Private Sub PrintHoraire(ByVal o As Object, ByVal e As PrintPageEventArgs)
        If Not PrintStarted Then
            pageNumber = 0
            pageCount = 1
            pageNoteCount = 0

            FillLists()
            PrintStarted = True

        End If
        PaintHoraire(e.Graphics, e.PageSettings)
        pageNumber += 1
        If listeEmployes.Count > 0 And listePlages.Count > 0 Then
            e.HasMorePages = True
        Else
            If Ckb_DescTaskRef.Checked AndAlso listePlagesReference.Count > 0 Then
                e.HasMorePages = True
            Else
                e.HasMorePages = False
                PrintStarted = False
            End If
        End If
    End Sub

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
        If Not renderThread Is Nothing Then
            If renderThread.IsAlive Then
                renderThread.Join()
            End If

        End If

        Using diag As New PrintPreviewDialog
            document.DefaultPageSettings.Landscape = document.PrinterSettings.DefaultPageSettings.Landscape
            

            diag.Document = document
            diag.ShowDialog()
        End Using
    End Sub

    Private Sub PictureBox1_Paint(sender As System.Object, e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
        e.Graphics.DrawImage(renderImage, 0, 0)
    End Sub

    Private Sub CheckBox1_CheckedChanged(sender As System.Object, e As System.EventArgs) Handles Ckb_Couleur.CheckedChanged, Ckb_DescTaskRef.CheckedChanged
        StartRender()
    End Sub

    Private Sub FillLists()
        Console.WriteLine("start fill list at " + Date.Now.ToString("m:ss.fff"))
        listePlages.Clear()
        listeEmployes.Clear()
        listeTaches.Clear()
        listePlagesReference.Clear()
        
        If Rb_ListEmp.Checked Then
            Dim selectedID As New List(Of Integer)
            For Each itm As ListViewItem In Lv_SelectionEmp.Items
                selectedID.Add(Convert.ToInt32(itm.Tag))
            Next
            listeEmployes = (From x In dbc.Employes Where x.Supprime = False _
                                AndAlso selectedID.Contains(x.ID) Order By x.Nom, x.Prenom Select x).ToList()
        Else
            listeEmployes = (From x In dbc.Employes Where x.Supprime = False Order By x.Nom, x.Prenom Select x).ToList()
        End If

        listePlages = (From x In dbc.PlageHoraires _
                        Where x.Date.DayOfYear >= Cal_Horaire.Value.DayOfYear AndAlso x.Date.DayOfYear < Cal_Horaire.Value.DayOfYear + 7 _
                        And x.Employe1.Supprime = False And listeEmployes.Contains(x.Employe1) Order By x.Date Select x).ToList()
        For Each task In (From x In listePlages Select x.Taches)
            listeTaches.AddRange(task)
        Next
        Console.WriteLine("end fill list at " + Date.Now.ToString("m:ss.fff"))
    End Sub

    Private Sub Rb_Employe_CheckedChanged(sender As System.Object, e As System.EventArgs) Handles Rb_ListEmp.CheckedChanged, Rb_AllEmp.CheckedChanged
        Btn_SelectEmp.Enabled = Rb_ListEmp.Checked
        Lv_SelectionEmp.Enabled = Rb_ListEmp.Checked
        If Rb_ListEmp.Checked Then
            If Lv_SelectionEmp.Items.Count > 0 AndAlso Not renderThread Is Nothing AndAlso Not renderThread.IsAlive Then
                StartRender()
            End If
        Else
            StartRender()
        End If
        
    End Sub

    Private Sub Btn_SelectEmp_Click(sender As System.Object, e As System.EventArgs) Handles Btn_SelectEmp.Click
        Using diag As New FrmEmployePicker
            Dim selectedID As New List(Of Integer)
            For Each itm As ListViewItem In Lv_SelectionEmp.Items
                selectedID.Add(Convert.ToInt32(itm.Tag))
            Next
            diag.SelectedEmployes = (From x In dbc.Employes Where (x.Supprime = False) _
                                AndAlso selectedID.Contains(x.ID) Select x).ToList()
            If diag.ShowDialog() = Windows.Forms.DialogResult.OK Then
                Lv_SelectionEmp.Items.Clear()
                For Each emp In diag.SelectedEmployes
                    Dim itm As New ListViewItem(emp.Nom.TrimStart() + ", " + emp.Prenom)
                    itm.Tag = emp.ID
                    Lv_SelectionEmp.Items.Add(itm)
                Next
                If Lv_SelectionEmp.Items.Count > 0 AndAlso Not renderThread Is Nothing AndAlso Not renderThread.IsAlive Then
                    StartRender()
                End If
            End If
        End Using
    End Sub

    Private Sub AnimationTimer_Tick(sender As System.Object, e As System.EventArgs) Handles AnimationTimer.Tick
        Dim center As New PointF(262 / 2, 262 / 2)
        Dim dist As Single = 22
        Dim r As Single = 6

        Using g As Graphics = Graphics.FromImage(loadingImage)
            g.SmoothingMode = SmoothingMode.HighQuality
            g.Clear(SystemColors.ControlDark)
            For i = 1 To 8
                Dim pt As New PointF
                pt.X = center.X + dist * Math.Cos(((360 / 8) * (i + animFrame)) / (180 / Math.PI))
                pt.Y = center.Y + dist * Math.Sin(((360 / 8) * (i + animFrame)) / (180 / Math.PI))
                Dim col As Integer = (100 * (8 - i)) / 8 + 150
                g.FillEllipse(New SolidBrush(Color.FromArgb(col, col, col)), pt.X - r, pt.Y - r, r * 2, r * 2)
            Next
        End Using
        animFrame += 1
        Using g As Graphics = PictureBox1.CreateGraphics()

            g.DrawImageUnscaled(loadingImage, 0, 0)
        End Using
    End Sub

    Private Sub Nud_NbCopie_ValueChanged(sender As System.Object, e As System.EventArgs) Handles Nud_NbCopie.ValueChanged
        document.PrinterSettings.Copies = Nud_NbCopie.Value
        If Nud_NbCopie.Value > 1 Then
            Ckb_CopieAssem.Enabled = True
        Else
            Ckb_CopieAssem.Enabled = False
        End If
    End Sub

    Private Sub Ckb_CopieAssem_CheckedChanged(sender As System.Object, e As System.EventArgs) Handles Ckb_CopieAssem.CheckedChanged
        If Ckb_CopieAssem.Checked Then
            Pb_ModeCopie.Image = My.Resources.AssembledCopy
        Else
            Pb_ModeCopie.Image = My.Resources.NormalCopy
        End If
        document.PrinterSettings.Collate = Ckb_CopieAssem.Checked
    End Sub

    Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
        If document.PrinterSettings.IsValid Then
            document.Print()
        Else
            MsgBox("Paramètres d'imprimante invalides", MsgBoxStyle.Information)
        End If
    End Sub


    Private Sub CboFormatPapier_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles CboFormatPapier.SelectedIndexChanged
        document.DefaultPageSettings.PaperSize = listeFormat(CboFormatPapier.SelectedIndex)
        If Not (listeFormat(CboFormatPapier.SelectedIndex).Kind = PaperKind.Letter _
                Or listeFormat(CboFormatPapier.SelectedIndex).Kind = PaperKind.A4 _
                Or listeFormat(CboFormatPapier.SelectedIndex).Kind = PaperKind.Legal) Then
            PbAttention.Visible = True
            LblErreurFormat.Visible = True
        Else
            PbAttention.Visible = False
            LblErreurFormat.Visible = False
        End If
        StartRender()
    End Sub
End Class